package com.css.spring.formework.webmvc.servlet;

import com.css.spring.formework.annotation.Controller;
import com.css.spring.formework.annotation.RequestMapping;
import com.css.spring.formework.annotation.RequestParam;
import com.css.spring.formework.aop.ProxyUtils;
import com.css.spring.formework.context.ApplicationContext;
import com.css.spring.formework.webmvc.HandlerAdapter;
import com.css.spring.formework.webmvc.HandlerMapping;
import com.css.spring.formework.webmvc.ModelAndView;
import com.css.spring.formework.webmvc.ViewResolver;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class DispatchServlet extends HttpServlet {

//    private Map<String, HandlerMapping> handlerMapping = new HashMap<String, HandlerMapping>();

    private List<HandlerMapping> handlerMapping = new ArrayList<HandlerMapping>();
    private Map<HandlerMapping, HandlerAdapter> handlerAdatpers = new HashMap<HandlerMapping, HandlerAdapter>();
    private List<ViewResolver> viewResolvers = new ArrayList<ViewResolver>();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        String url = req.getRequestURI();
//        String contextPath = req.getContextPath();
//        url = url.replace(contextPath, "").replaceAll("/+", "/");
//        HandlerMapping handler = handlerMapping.get(url);
//        try {
//            ModelAndView modelAndView = (ModelAndView) handler.getMethod().invoke(handler.getController());
//
//        } catch (IllegalAccessException e) {
//            e.printStackTrace();
//        } catch (InvocationTargetException e) {
//            e.printStackTrace();
//        }
        try {
            resp.setCharacterEncoding("utf-8");
            this.doDispatch(req, resp);
        } catch (Exception e) {
            e.printStackTrace();
            PrintWriter writer = null;

            writer = resp.getWriter();
            writer.write(Arrays.toString(e.getStackTrace()));
            writer.close();
        }
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws IOException, InvocationTargetException, IllegalAccessException {
        HandlerMapping handlerMapping = getHandler(req);
        if (handlerMapping == null) {
            resp.getWriter().write("404");
            return;
        }

        HandlerAdapter handlerAdapter = getHandlerAdapter(handlerMapping);
        if (handlerAdapter == null) {
            resp.getWriter().write("404");
            return;
        }
        ModelAndView modelAndView = handlerAdapter.handle(req, resp, handlerMapping);

        processDispatchResult(resp, modelAndView);
    }

    private void processDispatchResult(HttpServletResponse resp, ModelAndView modelAndView) throws IOException {
        if (modelAndView == null) {
            return;
        }
        if (viewResolvers.isEmpty()) {
            return;
        }
        for (ViewResolver viewResolver : viewResolvers) {
            if (modelAndView.getViewName() == null || !modelAndView.getViewName().equals(viewResolver.getViewName())) {
                continue;
            }

            String out = viewResolver.viewResolver(modelAndView);
            if (out != null) {
                resp.getWriter().write(out);
            }
        }
    }

    private HandlerAdapter getHandlerAdapter(HandlerMapping handlerMapping) {
        if (handlerAdatpers.isEmpty()) {
            return null;
        }

        return this.handlerAdatpers.get(handlerMapping);
    }

    private HandlerMapping getHandler(HttpServletRequest req) {
        if (handlerMapping.isEmpty()) {
            return null;
        }

        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replace(contextPath, "").replaceAll("/+", "/");

        for (HandlerMapping mapping : handlerMapping) {
            Matcher matcher = mapping.getUrl().matcher(url);
            if (!matcher.matches()) {
                continue;
            }
            return mapping;
        }

        return null;
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        //css// 初始化IOC容器
        ApplicationContext applicationContext = new ApplicationContext(config.getInitParameter("contextConfigLocation"));
        initStrategies(applicationContext);
    }


    protected void initStrategies(ApplicationContext context) {
        //css// 九种策略
        //css// 针对每个用户请求，都会经过一些处理的策略之后，最终才能够有结果输出
        //css// 每种策略可以自定义干预，但是最终的结果都是一致
        //css// 传说中的spring mvc 的九大组件
        //css//文件上传解析
        initMultipartResolver(context);
        //css//本地化解析
        initLocaleResolver(context);
        //css// 主题解析
        initThemeResolver(context);
        //css// 通过HandlerMapping 将请求映射到处理器
        initHandlerMappings(context);
        //css// 通过HandlerAdapter 进行多类型的参数动态匹配
        initHandlerAdapters(context);
        //css// 如果执行过程中遇到异常，将交给HandlerExceptionResolver 进行解析
        initHandlerExceptionResolvers(context);
        //css// 直接解析请求到绘图明
        initRequestToViewNameTranslator(context);
        //css// 通过ViewResolver解析逻辑视图到具体视图实现
        initViewResolvers(context);
        //css// flash映射管理器
        initFlashMapManager(context);
    }

    private void initHandlerMappings(ApplicationContext context) {
        String[] beanNames = context.getBeanDefinitionNames();
        try {
            for (String beanName : beanNames) {
                Object proxy = context.getBean(beanName);
                Object controller = ProxyUtils.getTargetObject(proxy);

                Class<?> aClass = controller.getClass();
                if (!aClass.isAnnotationPresent(Controller.class)) {
                    continue;
                }
                String baseUrl = "";
                if (aClass.isAnnotationPresent(RequestMapping.class)) {
                    RequestMapping requestMapping = aClass.getAnnotation(RequestMapping.class);

                    baseUrl = requestMapping.value();

                }

                Method[] methods = aClass.getMethods();
                for (Method method : methods) {
                    if (!method.isAnnotationPresent(RequestMapping.class)) {
                        continue;
                    }
                    RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                    String regex = ("/" + baseUrl + requestMapping.value().replaceAll("\\*", ".*").replaceAll("/+", "/"));
                    Pattern pattern = Pattern.compile(regex);

                    handlerMapping.add(new HandlerMapping(controller, method, pattern));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void initHandlerAdapters(ApplicationContext context) {
        for (HandlerMapping mapping : handlerMapping) {
            Annotation[][] pa = mapping.getMethod().getParameterAnnotations();
            Map<String, Integer> paramMapping = new HashMap<String, Integer>();
            //css// 处理 RequestParam 参数
            for (int i = 0; i < pa.length; i++) {
                for (Annotation annotation : pa[i]) {
                    if (annotation instanceof RequestParam) {
                        String paramName = ((RequestParam) annotation).value();
                        if (!"".equals(paramName.trim())) {
                            paramMapping.put(paramName, i);
                        }
                    }
                }
            }
            Class<?>[] parameterTypes = mapping.getMethod().getParameterTypes();

            for (int i = 0; i < parameterTypes.length; i++) {
                Class<?> parameterType = parameterTypes[i];

                if (parameterType == HttpServletRequest.class
                        || parameterType == HttpServletResponse.class) {
                    paramMapping.put(parameterType.getName(), i);
                }

            }
            this.handlerAdatpers.put(mapping, new HandlerAdapter(paramMapping));
        }
    }

    private void initViewResolvers(ApplicationContext context) {
        String templateRoot = context.getConfig().getProperty("templateRoot");
        String file = this.getClass().getClassLoader().getResource(templateRoot).getPath();
        File templateRootDir = new File(file);
        for (File listFile : templateRootDir.listFiles()) {

            viewResolvers.add(new ViewResolver(listFile.getName(), listFile));
        }
    }

    //css// //css// //css// //css// //css// //css// //css// //css// //css//

    private void initFlashMapManager(ApplicationContext context) {

    }


    private void initRequestToViewNameTranslator(ApplicationContext context) {

    }

    private void initHandlerExceptionResolvers(ApplicationContext context) {

    }

    private void initThemeResolver(ApplicationContext context) {

    }

    private void initLocaleResolver(ApplicationContext context) {

    }

    private void initMultipartResolver(ApplicationContext context) {

    }

}
